package com.yanghui.dtracing.core.log;

import com.yanghui.dtracing.core.PluginResourceResolver;
import com.yanghui.dtracing.core.classLoader.BreakParentClassLoader;
import com.yanghui.dtracing.core.config.AgentConfig;
import com.yanghui.dtracing.core.utils.AgentPackagePath;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


public class LogManager {
	
	private static BreakParentClassLoader logClassLoader;
	
	private static ILog noopLog = new NoopLog();
	
	private static String logDefineClassName;
	
	private static Map<Class<?>,ILog> logMap = new ConcurrentHashMap<Class<?>, ILog>();
	
	public static ILog getNoopLog() {
		return noopLog;
	}
	
	public static ClassLoader getLogClassLoader() {
		return logClassLoader;
	}
	
	public static void init() {
		try {
			String logImplementPath = AgentPackagePath.getPath().getAbsolutePath()
					+ File.separator + "logPlugin" 
					+ File.separator;
			File file = new File(logImplementPath);
			if(!file.exists() || file.isFile()) {
				noopLog.info("The logging plugin directory does not exist");
				return;
			}
			List<URL> jarUrls = new ArrayList<>();
			for(File child : file.listFiles()) {
				if(child.isFile() && child.getName().endsWith(".jar")){
					jarUrls.add(new URL("file:" + child.getAbsolutePath()));
				}
			}
			logClassLoader = new BreakParentClassLoader(jarUrls.toArray(new URL[] {}),LogManager.class.getClassLoader());
			PluginResourceResolver resolver = new PluginResourceResolver();
			List<URL> urls = resolver.getLogDefineResource();
			if(urls.isEmpty()) {
				noopLog.info("The logging plugin does not exist");
				return;
			}
			List<String> logDefines = resolver.getDefines(urls.get(0));
			if(logDefines.isEmpty()) {
				return;
			}
			logDefines= logDefines.stream().filter(item -> !item.startsWith("#")).collect(Collectors.toList());
			logDefineClassName = logDefines.get(0);
			noopLog.info("Load the name of the locator to the log：" + logDefineClassName);
		} catch (Throwable t) {
			noopLog.error("An exception has occurred in the loading log adapter",t);
		}
	}
	
	public static ILog getLogger(Class<?> clz) {
		if(null == logDefineClassName || "".equals(logDefineClassName)) {
			return noopLog;
		}
		if(logMap.get(clz) != null) {
			return logMap.get(clz);
		}
		try {
			Class<?> logClz = logClassLoader.loadClass(logDefineClassName);
			ILog log = (ILog)logClz.getConstructor(Class.class).newInstance(clz);
			logMap.putIfAbsent(clz, log);
			return log;
		} catch (Throwable t) {
			noopLog.error("Instantiate log adapter exceptions",t);
			return noopLog;
		}
	}
	
	public static void setLogContext(AgentConfig config) {
		try {
			String path;
			System.setProperty(AgentConfig.AGENT_LOG_LEVEL, config.getLogLevel());
			if(config.getLogPath() == null || "".equals(config.getLogPath())) {
				path = AgentPackagePath.getPath().getPath() + File.separator + "logs";
			}else {
				path = config.getLogPath();
			}
			System.setProperty(AgentConfig.AGENT_LOG_PATH,path);
			System.setProperty(AgentConfig.SERVICE_NAME,config.getServiceName());
		} catch (Exception e) {
			noopLog.error("Setting the log context has an exception",e);
		}
	}
}
